﻿using Microsoft;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using SimpleLiteForm;
using System.Globalization;

namespace SimpleLiteForm.Translator
{
    /// <summary>
    /// The translation service.
    /// </summary>
    public class BingTranslationService : ITranslationService
    {
        /// <summary>
        /// The singleton instance.
        /// </summary>
        private static ITranslationService _instance;

        /// <summary>
        /// The string for unknown translations.
        /// </summary>
        public const string UNKNOWN_TRANSLATION = "?";

        /// <summary>
        /// This is the service root uri for the Microsoft Translator service.
        /// </summary>
        private readonly Uri SERVICE_ROOT_URI = new Uri("https://api.datamarket.azure.com/Bing/MicrosoftTranslator/");
  
        /// <summary>
        /// This is the Account Key I generated for this app.
        /// </summary>
        private const string ACCOUNT_KEY = "vsKo477MaO2oOd195LUUWt1rIRqIrlmh5AdQJ+NAC/w";

        /// <summary>
        /// The translation-cache, where the key is t_language->originalstring, e.g: en->optimization (everything in lower case)
        /// </summary>
        private readonly Dictionary<string, string> _cache = new Dictionary<string, string>();
  
        /// <summary>
        /// The TranslatorContainer gives us access to the Microsoft Translator services.
        /// </summary>
        private TranslatorContainer _tranlatinContainer;
 
        /// <summary>
        /// Creates a new TranslationService instance.
        /// </summary>
        private BingTranslationService()
        {
            // the TranslatorContainer gives us access to the Microsoft Translator services 
            _tranlatinContainer = new TranslatorContainer(SERVICE_ROOT_URI); 
  
            // Give the TranslatorContainer access to your subscription 
            _tranlatinContainer.Credentials = new NetworkCredential(ACCOUNT_KEY, ACCOUNT_KEY);
        }

        /// <summary>
        /// Translates a string with auto source language detection.
        /// </summary>
        /// <param name="inputString">The input string to translate.</param>
        /// <param name="targetLanguage">The target language to tranlsate to.</param>
        /// <returns>The translated text or <code>UNKNOWKN_TRANSLATION</code> if not successful.</returns>
        public string Translate(string inputString, CultureInfo targetLanguage)
        {
            return Translate(inputString, targetLanguage, null);
        }

        /// <summary>
        /// Translates a string.
        /// </summary>
        /// <param name="inputString">The input string to translate.</param>
        /// <param name="targetLanguage">The target language to tranlsate to.</param>
        /// <param name="sourceLanguage">The source language to tranlate from.</param>
        /// <returns>The translated text or <code>UNKNOWKN_TRANSLATION</code> if not successful.</returns>
        public string Translate(string inputString, CultureInfo targetLanguage, CultureInfo sourceLanguage)
        {
            string key = string.Format("{0}->{1}", targetLanguage.TwoLetterISOLanguageName.ToLowerInvariant(), inputString.ToLowerInvariant());

            // check cache
            if (_cache.ContainsKey(key))
            {
                Debug.WriteLine("[Bing Translate] cache hit!");
                return _cache[key];
            }

            // Generate the query 
            var translationQuery = _tranlatinContainer.Translate(
                inputString,
                targetLanguage.TwoLetterISOLanguageName,
                (sourceLanguage == null) ? null : sourceLanguage.TwoLetterISOLanguageName);

            // Call the query and get the results as a List 
            var translationResults = translationQuery.Execute().ToList();

            string translationResult;

            if (translationResults.Count() == 0)
            {
                translationResult = UNKNOWN_TRANSLATION;
            }
            else
            {
                translationResult = translationResults.First().Text;
            }

            // add to cache
            _cache.Add(key, translationResult);

            // In case there were multiple results, pick the first one 
            return translationResult; 
        }

        /// <summary> 
        /// Uses the TranslatorContainer to identify the Language in which inputString  
        /// was written</summary> 
        /// <param name="inputString">The string to identify</param> 
        /// <returns>The Language Code for a language that this string could represent, 
        /// or null if one is not found.</returns> 
        public string DetectSourceLanguage(string inputString)
        {
            // calling Detect gives us a DataServiceQuery which we can use to call the  
            // service 
            var translateQuery = _tranlatinContainer.Detect(inputString);

            // since this is a console application, we do not want to do an asynchronous  
            // call to the service. Otherwise, the program thread would likely terminate 
            // before the result came back, causing our app to appear broken. 
            var detectedLanguages = translateQuery.Execute().ToList();

            // since the result of the query is a list, there might be multiple 
            // detected languages. In practice, however, I have only seen one. 
            // Some input strings, 'hi' for example, are obviously valid in  
            // English but produce other results, suggesting that the service 
            // only returns the first result. 
            if (detectedLanguages.Count() > 1)
            {
                Debug.WriteLine("Possible source languages:");

                foreach (var language in detectedLanguages)
                {
                    Debug.WriteLine("\t" + language.Code);
                }
            }

            // only continue if the Microsoft Translator identified the source language 
            // if there are multiple, let's go with the first. 
            if (detectedLanguages.Count() > 0)
            {
                return detectedLanguages.First().Code;
            }

            else
            {
                return null;
            }
        }

        /// <summary>
        /// Gets a list of the supported languages.
        /// </summary>
        /// <returns>The supported language list.</returns>
        public IList<string> GetSupportedLanuages()
        {
            // Generate the query 
            var languagesForTranslationQuery = _tranlatinContainer.GetLanguagesForTranslation();
            var languageList = new List<string>();

            foreach (var language in languagesForTranslationQuery.Execute())
            {
                languageList.Add(language.Code);
            }

            return languageList;
        }

        /// <summary>
        /// Clears the cache.
        /// </summary>
        public void ClearCache()
        {
            _cache.Clear();
        }

        /// <summary>
        /// Gets the translation service instance.
        /// </summary>
        public static ITranslationService Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new BingTranslationService();
                return _instance;
            }
        }
    }
}
